package memnet.graph;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class Graph {

	private Map<Object, Node> nodes = new HashMap<Object, Node>();
	
	/**
	 * Returns the node corresponding to the object data,
	 * or creates one if it does not exist.
	 * O(1) time
	 * @param data
	 * @return
	 */
	public Node getNode(Object key){
		Node n = nodes.get(key);
		if( n == null){
			n = new Node(key);
			nodes.put(key, n);
		}
		return n;
	}
	
	/**
	 * Creates or updates an edge between the two nodes identified by key1 and key2
	 * @param key1
	 * @param key2
	 * @param edgeData
	 * @param weight
	 */
	public void setEdge( Object key1, Object key2, Object edgeData, double weight){
		
		getNode(key1).setEdge(getNode(key2), new EdgeData(edgeData), weight);
	}
	
	/**
	 * Returns the weight of edge connecting src to dest that is identified by
	 * edgeData
	 * @param key1
	 * @param key2
	 * @param edgeData
	 * @return
	 */
	public double edgeWeight(Object src, Object dst, Object edgeData){
		Edge e= getNode(src).getOutEdge(getNode(dst),	new EdgeData(edgeData));
		if( e==null){
			throw new GraphError("Edge "+e+" does not exist");
		}
		return e.weight;
	}
	
	/**
	 * Removes a node completely from the graph
	 * @param data
	 */
	public void removeNode(Object key){
		nodes.get(key).disconnect();
		nodes.remove(key);
	}
	
	public Set<Edge> getOutgoingEdges(Object nodekey){
		return (getNode(nodekey).getOutgoingEdges());
	}
	
	public Set<EdgeData> getOutgoingEdgedata(Object nodekey){
		return (Set<EdgeData>)toEdgeData( getOutgoingEdges(nodekey));
	}
	
	
	public Set<Edge> getEdges(Object src, Object dst){
		return (getNode(src)).getOutEdges(getNode(dst));
	}
	
	public Set<EdgeData> getConnectingEdges(Object src, Object dst){
		return (Set<EdgeData>)toEdgeData( getEdges(src,dst));
	}
	
	private static Set<EdgeData> toEdgeData(Set<Edge> edgeSet){
		Set<EdgeData> retval = new HashSet<EdgeData>();
		for( Edge edge : edgeSet){
			retval.add(edge.data);
		}
		return retval;
	}
	
	public String debugInfo(){
		StringBuilder sbBuilder = new StringBuilder();
		for( Object key : new HashSet<Object>(nodes.keySet())){
			Node n = nodes.get(key);
			sbBuilder.append(key+":");
			sbBuilder.append(n+". Outedges:");
			
			sbBuilder.append(n.getOutgoingEdges());
			
			sbBuilder.append("\n");
		}
		return sbBuilder.toString();
	}
	
}
